/***************************************************************************************/
/* Input: A piece of C/C++ source code.                                                */
/* Output: 1.Keywords.                                                                 */
/*         2.Decimal, octal and hexadecimal numbers.                                   */
/*         3.Identifiers.                                                              */
/*         4.Position information of 1, 2, and 3. The format should be like:           */
/*           (row_start, column_start, row_end, column_end)                            */
/*                                                                                     */
/* E.g. Input:                                                                         */
/*              -int x1, x2 = 0x1f;                                                    */
/*              -float y1;                                                             */
/*      Output:                                                                        */
/*              -x1 (0, 4, 0, 5)                                                       */ 
/*              -x2 (0, 8, 0, 9)                                                       */
/*              -31 (0, 13, 0, 16)                                                     */
/*              -y1 (1, 6, 1, 7)                                                       */
/***************************************************************************************/

#include <string>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <iomanip>
using namespace std; 

#define MAX 38

/* ANSI C keywords */
string Key[] = {"auto", "short", "int", "long", "float", "double", "char", "struct", "union",
"enum", "typedef", "const", "unsigned", "signed", "extern", "register", "static",
"volatile", "void", "if", "else", "switch", "case", "for", "do", "while", "goto",
"continue", "break", "default", "sizeof", "return", "inline", "restrict", "bool",
"imaginary", "generic", "complex"};

/*identify letters*/
int IsLetter(char c)    
{        
	if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) 
		return 1;
	else 
		return 0;
}

/*identify digits*/
int IsDigit(char c)    
{          
	if (c >= '0' && c <= '9') 
		return 1;
	else 
		return 0;
}

/* identify keywords */
int IsKey(string c)     
{          
	int i;
	for (i = 0; i < MAX; i++) 
	{
		if (Key[i].compare(c) == 0) 
			return 1;      
	} 
	return 0;
}

/* analyse() */
void analyse(FILE *f)
{
	long int a;  
	double b;
	char *str1;
	int row = 0, col = 0, col1, col2; 
	char ch = ' ';                     /* read characters one by one */
	string str = "";                   
	while ((ch = fgetc(f)) != EOF) 
	{
		str="";                      

		/* "\n" is a signal for new line */
		if (ch == '\n')
		{
			row++;                     
			col = 0;                    
		}  

		/* each identifier, keyword and number are separated by a single space in the C/C++ source code*/
		else if (ch == 32)
		{   
			while (ch == 32)
			{	    
				ch = fgetc(f);
				col++;
			}
			fseek(f, -1L, SEEK_CUR);
		}
        
        /* identifiers may start with "_" or "0~9" */
		else if ((ch == '_'))
		{
			col1 = col;
			while (IsLetter(ch) || IsDigit(ch) || (ch == '_'))
			{ 	 
				str = str + ch;		
				ch = fgetc(f);
				col++;
			}
			col2 = col - 1; 
			fseek(f, -1L, SEEK_CUR);
			cout<<str<<"\t"<<"("<<row<<","<<col1<<","<<row<<","<<col2<<")"<<endl;
		}

		/* recognize identifiers */
		else if (IsLetter(ch))
		{
			col1 = col;
			str = str + ch;		
			ch = fgetc(f);
			col++;
			while (IsLetter(ch) || IsDigit(ch) || (ch == '_'))
			{ 	 
				str = str + ch;		
				ch = fgetc(f);
				col++;
			}
			col2 = col - 1; 
			if (IsKey(str) == 0) 
				cout<<str<<"\t"<<"("<<row<<","<<col1<<","<<row<<","<<col2<<")"<<endl;
			fseek(f, -1L, SEEK_CUR);
		}

		/* identify positive and negative numbers */
		else if (IsDigit(ch) || (ch == '-') || (ch == '+'))
		{
			col1 = col;
			while (IsDigit(ch) || (ch == '-') || (ch == '+'))
			{
				switch (ch)
				{
				case'-':
				case'+':
					{
						str = str + ch;
						ch = fgetc(f);
						col++;
					}
				default:
					{
						if (ch == '0')   /* octal or hexadecimal numbers may start with "0" */
						{
							str = str + ch;
							ch = fgetc(f); 
							col++;
							
							/* identify octal number */
							if (IsDigit(ch))    
							{
								while (ch >= '0' && ch <= '7')
								{
									str = str + ch;
									ch = fgetc(f);
									col++;
								}
								col2 = col - 1;
								a = strtol(str.c_str(), &str1, 8);
								cout<<dec<<a<<"\t("<<row<<","<<col1<<","<<row<<","<<col2<<")"<<endl;
								fseek(f, -1L, SEEK_CUR);
							}
							/* identify hexadecimal numbers */
							else if ((ch == 'x') || (ch == 'X'))
							{
								str = str + ch;
								ch = fgetc(f);
								col++;
								while (IsDigit(ch) || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))
								{
									str = str + ch;			 
									ch = fgetc(f);
									col++;
								}
								col2 = col - 1;
								a = strtol(str.c_str(), &str1, 16);  
								cout<<dec<<a<<"\t("<<row<<","<<col1<<","<<row<<","<<col2<<")"<<endl; 
								fseek(f, -1L, SEEK_CUR);
							}
							/* identify floating numbers */
							else
							{
								while (IsDigit(ch) || (ch == '.') || ((ch == 'e') || (ch == 'E')))
								{
									str = str + ch;
									ch = fgetc(f);
									col++;
								}
								col2 = col - 1;
								b = atof(str.c_str());  
								cout<<b<<"\t("<<row<<","<<col1<<","<<row<<","<<col2<<")"<<endl; 
								fseek(f, -1L, SEEK_CUR);
							}
						}
						/* identify decimal numbers */
						else if (IsDigit(ch) && (ch != '0'))
						{
							col1 = col;
							while (IsDigit(ch) || (ch == '.') || ((ch == 'e') || (ch == 'E')))
							{
								str = str + ch;
								ch = fgetc(f);
								col++;
							}
							col2 = col - 1;
							b = atof(str.c_str()); 
							cout<<b<<"\t("<<row<<","<<col1<<","<<row<<","<<col2<<")"<<endl; 
							fseek(f, -1L, SEEK_CUR);
						}    
					}
				}
			}			
		}

		/* other characters */
		else switch (ch)
		{          
			case'*' :
			case'=' :
			case'/' :
			case'(' :
			case')' :
			case'[' :
			case']' :         
			case';' :
			case'.' :
			case',' :
			case'{' :
			case'}' :
			case':' :
			case'>' :
			case'<' :
				{
					ch = fgetc(f);
					col++;
					fseek(f, -1L, SEEK_CUR);
				} break;
			default :
				{
					ch = fgetc(f);
					col++;
					fseek(f, -1L, SEEK_CUR);
				};
		}
	}
}

int main()
{
	char in_fn[30];
	FILE * f;
	cout<<"Please input the filename and directory:"<<endl;
	for (;;)
	{
		cin>>in_fn;
		if((f = fopen(in_fn,"r")) != NULL) 
			break;
		else 
		{
			cout<<"Error! No such file or directory."<<endl;
			cout<<"Please input the filename and directory again:"<<endl;		
		}
	}
	cout<<"\n********************Result of the scanner*********************"<<endl;
	analyse(f);
	fclose(f);
	return 0; 
}